package com.alex.controller;

import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.util.NamedValue;
import com.alex.common.beans.ResultBean;
import com.alex.common.config.IndexConfig;
import com.alex.entity.*;
import com.alex.paramVo.ArticleSearchVo;
import com.alex.service.AllQueryBasicService;
import com.alex.service.FilterService;
import com.alex.utils.*;
import jakarta.json.stream.JsonGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.time.Year;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@RestController
@RequestMapping("/search")
@SuppressWarnings(value = "all")
@Slf4j
public class SearchController {


    private static ElasticsearchClient client = EsClient.client();

    @Resource
    private FilterService filterService;

    /**
     * 分页
     *
     * @param page
     * @param limit
     * @return
     */
    public static List<Integer> checkPage(Integer page, Integer limit) {
        if (page == null) page = 0;
        if (limit == null) limit = 10;
        List<Integer> list = new ArrayList<>();

        list.add((page - 1) * limit);
        //list.add(((page - 1) * limit) + limit);
        list.add(limit);
        return list;
    }

    public static int convertToInt(double num) {
        try {
            return (int) num;
        } catch (Exception e) {
            // 发生异常时返回 0
            return 0;
        }
    }

    /**
     * 核心检索
     *
     * @param search
     * @return
     * @throws Exception
     */
    @PostMapping(value = "search")
    public ResultBean<Object> search(@RequestBody ArticleSearchVo searchVo) throws Exception {

        if (StringUtil.isEmpty(searchVo.getPram())) {
            return new ResultBean<>(3, "请输入正确的检索词");
        }
        if (searchVo.getPage() == null) {
            searchVo.setPage(1);
        }
        if (searchVo.getLimit() == null) {
            searchVo.setLimit(10);
        }
        //默认年份排序
        if (searchVo.getShortFiled() == null) {
            searchVo.setShortFiled(2);
        }
        //默认排倒序
        if (searchVo.getShortOrder() == null) {
            searchVo.setShortOrder(2);
        }


        /**
         * 增加检索记录
         */
        try {
            Db.use("ai_study").insert(Entity.create("search_history_es")
                    .set("uid", searchVo.getUid())
                    .set("gid", searchVo.getGid())
                    .set("real_name", searchVo.getRealName())
                    .set("content", searchVo.getPram())
            );
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            System.out.println("增加检索记录");
        }


        Query query;

        List<ArticleEntity> resultList = new ArrayList<>();
        if (StringUtil.isEmpty(searchVo.getPram())) {
            BoolQuery build = new BoolQuery.Builder().build();
            query = build._toQuery();
            System.out.println("检索词为空");
        } else if (searchVo.getPram().startsWith("id=")) {
            String param = searchVo.getPram();
            param = param.replace("id=", "");
            if (CheckUtil.isNumberRange(param)) {
                String[] split = param.split(",");
                List<FieldValue> values = new ArrayList<>();
                for (String id : split) {
                    values.add(FieldValue.of(id));
                }
                Query idsQuery = TermsQuery.of(t -> t.field("id").terms(new TermsQueryField.Builder().value(values).build()))._toQuery();
                BoolQuery.Builder allQueryBuilder = new BoolQuery.Builder();
                allQueryBuilder.must(idsQuery);
                query = allQueryBuilder.build()._toQuery();

            } else {
                query = null;
            }
        } else {
            BoolQuery.Builder boolQuery = filterService.getBoolQuery(searchVo);
            query = boolQuery.build()._toQuery();
        }
        //排序字段 默认 1 相关度
        if (searchVo.getShortFiled() == null) {
            searchVo.setShortFiled(1);
        }
        //排序方式 默认 1 正序
        if (searchVo.getShortOrder() == null) {
            searchVo.setShortOrder(1);
        }
        String sorter = "";
        if (searchVo.getShortFiled() == 2) {
            sorter = "year";
        }
        if (searchVo.getShortFiled() == 3) {
            sorter = "citationquota";
        }
        if (searchVo.getShortFiled() == 4) {
            sorter = "jcr_if";
        }

        //获取 SearchRequest对象
        SearchRequest.Builder builder = new SearchRequest.Builder();

        List<String> includes = Stream.of("id", "title",
                "journal", "ab", "pmid", "doi", "ut", "cn_ab",
                "year", "cn_ti", "jcr_if", "zkydalei", "jid", "cite",
                "key_word_list", "info",
                "citationquota", "auth", "cn_unit").collect(Collectors.toList());

        //如果是写作助手返回回来的,增加返回字段
        if (StringUtil.isNotNull(searchVo.getType())) {
            if (searchVo.getType().equals("QF_Study_Ai_Writing_Assistant")) {
                includes.add("background");
                includes.add("methods");
                includes.add("results");
                includes.add("discussion");
                includes.add("cn_background");
                includes.add("cn_methods");
                includes.add("cn_results");
                includes.add("cn_discussion");
            }
        }

        builder.index(IndexConfig.CORE_INDEX);
        builder.query(query);


        if (searchVo.getShortFiled() != 1) {
            String finalSorter = sorter;
            builder.sort(sortOptionsBuilder -> sortOptionsBuilder.field(fieldSortBuilder -> fieldSortBuilder.field(finalSorter).order(searchVo.getShortOrder() == 1 ? SortOrder.Asc : SortOrder.Desc)));
        }
        List<Integer> pageListIds = checkPage(searchVo.getPage(), searchVo.getLimit());
        SearchRequest searchRequest = builder
                .source(sourceConfigBuilder -> sourceConfigBuilder.filter(sourceFilterBuilder -> sourceFilterBuilder.includes(includes)))
                .highlight(highlightBuilder -> highlightBuilder.preTags("<span style=\"color:red\">").postTags("</span>")
                        .fields("title", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_ti", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("ab", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("discussion", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("results", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("methods", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("background", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))

                        .fields("cn_discussion", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_results", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_methods", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_background", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                )
                .trackTotalHits(k -> k.enabled(true))
                .from(pageListIds.get(0))
                .size(pageListIds.get(1))
                //.preference("_prefer_node")
                .build();
        //打印DSL
        printEsBySearchRequest(searchRequest);

        SearchResponse<ArticleEntity> response = client.search(searchRequest, ArticleEntity.class);


        List<Hit<ArticleEntity>> hits = response.hits().hits();
        //处理返回结果的高亮
        for (Hit<ArticleEntity> hit : response.hits().hits()) {
            ArticleEntity source = hit.source();
            Map<String, List<String>> highlight = hit.highlight();
            highlight.forEach((k, v) -> {
                if (k.equals("title")) {
                    source.setTitle(v.get(0));
                } else if (k.equals("cn_ti")) {
                    source.setCn_ti(v.get(0));
                } else if (k.equals("cn_discussion")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_discussion(v);
                } else if (k.equals("cn_results")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_results(v);
                } else if (k.equals("cn_methods")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_methods(v);
                } else if (k.equals("cn_background")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_background(v);
                } else if (k.equals("discussion")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setDiscussion(v);
                } else if (k.equals("results")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setResults(v);
                } else if (k.equals("methods")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setMethods(v);
                } else if (k.equals("background")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setBackground(v);
                } else if (k.equals("ab")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setAb(stringBuilder.toString());
                } else if (k.equals("cn_ab")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_ab(stringBuilder.toString());
                }
            });
        }


        /**
         * 开始封装返回结果
         */
        long value = response.hits().total().value();
        System.out.println("命中数量_" + value);
        PageList<ArticleEntity> pageList = new PageList<>();
        pageList.setUuid(null);
        pageList.setTotalElements(value);


        //根据来源判断是否增加诚信信息
//        if ("QF_Study_Cn_Pubmed".equalsIgnoreCase(searchVo.getType())) {
//
//            for (Hit<ArticleEntity> hit : hits) {
//                ArticleEntity source = hit.source();
//                /**
//                 * 封装诚信数据
//                 */
//                try {
//                    List<Entity> list = Db.use("ai_study").query("SELECT " +
//                            "`id`,`publishing_unit`,`warning_level`," +
//                            "`year_of_publication`,`publication_date`," +
//                            "`subject`,`source_url`,`journal_name`,`da_lei` " +
//                            "from early_warning_journal WHERE jid = " + hit.source().getJid());
//                    if (!list.isEmpty()) {
//                        Entity entity = list.get(0);
//                        if (null != entity) {
//                            source.setEntity(entity);
//                        }
//                    }
//                } catch (SQLException e) {
//                    e.printStackTrace();
//                }
//                resultList.add(source);
//            }
//        } else {
//            System.out.println("非PM,直接返回,不去检索诚信信息");
//            resultList = AllQueryBasicService.coventList(hits);
//        }

        resultList = AllQueryBasicService.coventList(hits);
        for (ArticleEntity articleEntityV2 : resultList) {
            try {
                articleEntityV2.setInfo(toTitleCase(articleEntityV2.getInfo()));
                List<String> auth = articleEntityV2.getAuth();
                if (auth.size() == 1 && auth.get(0).equals(",")) {
                    articleEntityV2.setAuth(new ArrayList<>());
                }
            } catch (Exception e) {
            }
        }
        if (null != searchVo.getType()) {
            if ("HIGH_SCORING_TOPICS".equalsIgnoreCase(searchVo.getType())) {
                resultList.removeIf(i -> i.getAb().equals(""));
                resultList.removeIf(i -> null == i.getAb());
            }
        }
        try {
            resultList.forEach(articleEntity -> {
                for (String s : articleEntity.getDiscussion()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getMethods()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getBackground()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getResults()) {
                    s = s.replaceAll("ĺļ", "");
                }

                for (String s : articleEntity.getCn_discussion()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getCn_methods()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getCn_background()) {
                    s = s.replaceAll("ĺļ", "");
                }
                for (String s : articleEntity.getCn_results()) {
                    s = s.replaceAll("ĺļ", "");
                }
            });
        } catch (Exception e) {
            // e.printStackTrace();
        }

        pageList.setList(resultList);
        pageList.setTotalPages((int) (value / searchVo.getLimit() < 1 ? 1 : (value / searchVo.getLimit()) + 1));//总页数
        pageList.setCurrentPage(searchVo.getPage());//当前页
        pageList.setPageSize(searchVo.getLimit());//每页显示条数

        if (null != searchVo.getType()) {
            /**
             * 课题思路助手
             */
            if ("QF_Study_Topic_Idea_Assistant".equalsIgnoreCase(searchVo.getType())) {
                pageList.setDrug(filter_Str(query, "drug.keyword", 30));
                pageList.setGene(filter_Str(query, "gene.keyword", 30));
                pageList.setPathway(filter_Str(query, "pathway.keyword", 30));
                pageList.setDisease(filter_Str(query, "disease.keyword", 30));
                pageList.setCountry(filter_Str(query, "cn_country.keyword", 30));
                pageList.setYear(filter_Long_re(query, "year", 50));
            }
            /**
             * 中文 pubmed
             */
            if ("QF_Study_Cn_Pubmed".equalsIgnoreCase(searchVo.getType())) {


                pageList.setJcrif(jcr_if(query));

                pageList.setZkydalei(filter_Long(query, "zkydalei", 30));

                pageList.setJcrquartile(filter_Long(query, "jcr_quartile", 30));

                pageList.setAuthor(filter_Str(query, "auth.keyword", 30));

                pageList.setCountry(filter_Str(query, "cn_country.keyword", 30));

                pageList.setUnit(filter_Str(query, "cn_unit.keyword", 30));

                pageList.setPt(filter_Str(query, "pt.keyword", 30));

                pageList.setJournal(filter_Str(query, "journal.keyword", 30));

                pageList.setYear(filter_Long(query, "year", 50));


                pageList.setDrug(filter_Str(query, "drug.keyword", 30));
                pageList.setGene(filter_Str(query, "gene.keyword", 30));
                pageList.setPathway(filter_Str(query, "pathway.keyword", 30));
                pageList.setDisease(filter_Str(query, "disease.keyword", 30));
            }

            /**
             * AI写作助手
             */
            if ("QF_Study_Ai_Writing_Assistant".equalsIgnoreCase(searchVo.getType())) {
                pageList.setCnyear(filter_Long(query, "year", 100));
            }
        }

        String uuid = UUID.randomUUID().toString();
        RedisUtil.saveObject(uuid, searchVo);
        pageList.setUuid(uuid);

        return new ResultBean<>(pageList);
    }

    public static List<AggsKeyValue> filter_StrNSFCDEPT(Query query, String filter, Integer count, String dept) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEXNFSC)
                        .query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        for (StringTermsBucket bucket : response.aggregations().get("my_aggre").sterms().buckets().array()) {
            long value = bucket.docCount();
            list.add(new AggsKeyValue(bucket.key().stringValue(), value));
        }

        List<AggsKeyValue> collect = list.stream().filter(i -> StringUtil.isNotNull(i)).sorted(Comparator.comparing(AggsKeyValue::getValue).reversed()).collect(Collectors.toList());
        if (StringUtil.isNotNull(dept)) {
            collect = collect.stream().filter(i -> i.getKey().contains(dept)).collect(Collectors.toList());
        }
        return collect;
    }

    /**
     * 如果检索词中不包含数字和字母
     *
     * @param string
     * @return
     */
    public boolean reg(String string) {

        Matcher matcher_yh = Pattern.compile("[0-9a-zA-Z]+").matcher(string);

        while (matcher_yh.find()) {
            return true;
        }
        return false;
    }

    // 判断是否包含中文汉字
    public boolean isContainChinese(String str) {
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
        Matcher m = p.matcher(str);
        return m.find();
    }

    /**
     * 打印DLS
     *
     * @param searchRequest
     */
    public static void printEsBySearchRequest(SearchRequest searchRequest) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        JsonpMapper mapper = new JacksonJsonpMapper();
        JsonGenerator generator = mapper.jsonProvider().createGenerator(byteArrayOutputStream);
        mapper.serialize(searchRequest, generator);
        generator.close();
        System.out.println("打印的DSL___");
        System.out.println(byteArrayOutputStream.toString());

    }

    public static List<AggsKeyValue> filter_StrNSFC(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEXNFSC)
                        .query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        for (StringTermsBucket bucket : response.aggregations().get("my_aggre").sterms().buckets().array()) {
            long value = bucket.docCount();
            if (StringUtil.isNotNull(bucket.key().stringValue())) {
                if (filter.equals("gene.keyword")) {
                    list.add(new AggsKeyValue(bucket.key().stringValue().toUpperCase(), value));
                } else {
                    list.add(new AggsKeyValue(bucket.key().stringValue(), value));
                }

            }

        }

        List<AggsKeyValue> collect = list.stream().filter(i -> StringUtil.isNotNull(i)).sorted(Comparator.comparing(AggsKeyValue::getValue).reversed()).collect(Collectors.toList());
        return collect;
    }

    //放在一起处理 作者 单位 部门数量信息
    public static List<AggsKeyValue> filter_StrNSFCUnit(Query query, String filter, Integer count, String unit) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEXNFSC)
                        .query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        for (StringTermsBucket bucket : response.aggregations().get("my_aggre").sterms().buckets().array()) {
            long value = bucket.docCount();
            list.add(new AggsKeyValue(bucket.key().stringValue(), value));
        }

        List<AggsKeyValue> collect = list.stream().filter(i -> StringUtil.isNotNull(i)).sorted(Comparator.comparing(AggsKeyValue::getValue).reversed()).collect(Collectors.toList());
        if (StringUtil.isNotNull(unit)) {
            collect = collect.stream().filter(i -> i.getKey().contains(unit)).collect(Collectors.toList());
        }
        return collect;
    }


    /**
     * 核心检索
     *
     * @param search
     * @return
     * @throws Exception
     */
    @PostMapping(value = "search-nsfc")
    public ResultBean<Object> searchnSFC(@RequestBody ArticleSearchVo searchVo) throws Exception {
        // 未来学习中心 基金论文检索地址
        // 主要适配下老板的 新给的 数据
        //先验证参数
        //先验证参数
        if (searchVo.getPram().length() > 30000) {
            return new ResultBean<>(3, "参数超过限定长度,请输入正确的参数");
        }
//        if (StringUtil.isEmpty(searchVo.getPram())) {
//            return new ResultBean<>(3, "请输入正确的检索词");
//        }
        if (searchVo.getPage() == null) {
            searchVo.setPage(1);
        }
        if (searchVo.getLimit() == null) {
            searchVo.setLimit(10);
        }
        if (searchVo.getPage() == 10) {
            if (searchVo.getLimit() >= 1000) {
                searchVo.setLimit(1000);
            }
        }
        if (searchVo.getPage() == 20) {
            if (searchVo.getLimit() >= 500) {
                searchVo.setLimit(500);
            }
        }
        //默认年份排序
        if (searchVo.getShortFiled() == null) {
            searchVo.setShortFiled(2);
        }
        //默认排倒序
        if (searchVo.getShortOrder() == null) {
            searchVo.setShortOrder(2);
        }

        Query query = null;

        List<ArticleEntity> resultList = new ArrayList<>();
        if (StringUtil.isEmpty(searchVo.getPram())) {
            searchVo.setPram("");
            BoolQuery.Builder boolQuery = filterService.getBoolQueryNSFc(searchVo);
            query = boolQuery.build()._toQuery();
            System.out.println("检索词为空");
        } else if (searchVo.getPram().startsWith("id=")) {
            String param = searchVo.getPram();
            param = param.replace("id=", "");
            if (CheckUtil.isNumberRange(param)) {
                String[] split = param.split(",");
                List<FieldValue> values = new ArrayList<>();
                for (String id : split) {
                    values.add(FieldValue.of(id));
                }
                Query idsQuery = TermsQuery.of(t -> t.field("id").terms(new TermsQueryField.Builder().value(values).build()))._toQuery();
                BoolQuery.Builder allQueryBuilder = new BoolQuery.Builder();
                allQueryBuilder.must(idsQuery);
                query = allQueryBuilder.build()._toQuery();

            }
        } else {
            System.out.println("GR++++++++++++++++++++");
            BoolQuery.Builder boolQuery = filterService.getBoolQueryNSFc(searchVo);
            query = boolQuery.build()._toQuery();
        }
        //排序字段 默认 1 相关度
        if (searchVo.getShortFiled() == null) {
            searchVo.setShortFiled(1);
        }
        //排序方式 默认 1 正序
        if (searchVo.getShortOrder() == null) {
            searchVo.setShortOrder(1);
        }
        String sorter = "";
        if (searchVo.getShortFiled() == 2) {
            sorter = "year";
        }
        if (searchVo.getShortFiled() == 3) {
            sorter = "citationquota";
        }
        if (searchVo.getShortFiled() == 4) {
            sorter = "jcr_if";
        }

        //获取 SearchRequest对象
        SearchRequest.Builder builder = new SearchRequest.Builder();

        List<String> includes = Stream.of("id", "title", "journal", "ab", "unit", "unit_dept", "pmid", "doi", "ut", "cn_ab", "year", "cn_ti", "jcr_if", "zkydalei", "cite", "jid",
                "keyword", "info",
                "citationquota", "auth", "cn_unit").collect(Collectors.toList());
        //如果是写作助手返回回来的,增加返回字段


        builder.index(IndexConfig.CORE_INDEXNFSC);
        if (searchVo.getShortFiled() != 1) {
            String finalSorter = sorter;
            builder.sort(sortOptionsBuilder -> sortOptionsBuilder.field(fieldSortBuilder -> fieldSortBuilder.field(finalSorter).order(searchVo.getShortOrder() == 1 ? SortOrder.Asc : SortOrder.Desc)));
        }

        List<Integer> pageListIds = checkPage(searchVo.getPage(), searchVo.getLimit());

        SearchRequest searchRequest = builder.query(query)
                .source(sourceConfigBuilder -> sourceConfigBuilder.filter(sourceFilterBuilder -> sourceFilterBuilder.includes(includes)))
                .highlight(highlightBuilder -> highlightBuilder.preTags("<span style=\"color:red\">").postTags("</span>")
                        .fields("title", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_ti", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("ab", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))
                        .fields("cn_ab", highlightFieldBuilder -> highlightFieldBuilder.numberOfFragments(0).fragmentSize(1000))

                )
                .trackTotalHits(k -> k.enabled(true))
                .from(pageListIds.get(0))
                .size(pageListIds.get(1))
                //.preference("_prefer_node")
                .build();


        //打印DSLprintEsBySearchRequest(searchRequest);

        SearchResponse<ArticleEntity> response = client.search(searchRequest, ArticleEntity.class);


        List<Hit<ArticleEntity>> hits = response.hits().hits();
        log.info("检索词+" + searchVo.getPram() + "++数量+" + hits.size());//0
        //处理返回结果的高亮
        for (Hit<ArticleEntity> hit : response.hits().hits()) {
            ArticleEntity source = hit.source();
            Map<String, List<String>> highlight = hit.highlight();
            highlight.forEach((k, v) -> {
                if (k.equals("title")) {
                    source.setTitle(v.get(0));
                } else if (k.equals("cn_ti")) {
                    source.setCn_ti(v.get(0));
                } else if (k.equals("ab")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setAb(stringBuilder.toString());
                } else if (k.equals("cn_ab")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    for (String s : v) {
                        stringBuilder.append(s);
                    }
                    source.setCn_ab(stringBuilder.toString());
                }
            });
        }
        //根据来源判断是否增加诚信信息
        if ("QF_Study_Cn_Pubmed".equalsIgnoreCase(searchVo.getType())) {

            for (Hit<ArticleEntity> hit : hits) {
                ArticleEntity source = hit.source();
                String cnTi = source.getCn_ti();
                if (cnTi != null) {
                    cnTi = cnTi.replace("ĺļ", "");
                    source.setCn_ti(cnTi);
                }
                resultList.add(source);
            }
        } else {
            System.out.println("非PM,直接返回,不去检索诚信信息");
            resultList = AllQueryBasicService.coventList(hits);
        }
        // 进行判断 如果最后一页 超过了删除
        Integer page = searchVo.getPage();
        Integer limit = searchVo.getLimit();
        int mix = page * limit;
        System.out.println("mix++" + mix);
        System.out.println(resultList.size());
        int index = 10000 - (page * limit);

        System.out.println("论文检索页码+" + page);
        System.out.println("论文检索limit+" + limit);
        System.out.println("aaaaindex+" + index);
        if (index < 0) {
            System.out.println("少于0 进行截取");
            try {
                resultList = resultList.subList(0, limit - (0 - index));
                System.out.println("截取后list数量" + resultList.size());
            } catch (Exception e) {

            }


//            resultList = resultList.subList(0, (0-index));
        }


        /**
         * 开始封装返回结果
         */
        long value = response.hits().total().value();
        System.out.println("命中数量_" + value);
        PageListNfsc<ArticleEntity> pageList = new PageListNfsc<>();
        pageList.setUuid(null);
        pageList.setTotalElements(value);
        for (ArticleEntity ArticleEntity_V2 : resultList) {
            if (StringUtil.isNotNull(ArticleEntity_V2.getUt())) {
                String ut = ArticleEntity_V2.getUt();
                ArticleEntity_V2.setUt(encryptionAid(ut));
            }
        }

        for (ArticleEntity articleEntityV2 : resultList) {
            articleEntityV2.setInfo(toTitleCase(articleEntityV2.getInfo()));
        }

        pageList.setList(resultList);
        pageList.setTotalPages((int) (value / searchVo.getLimit() < 1 ? 1 : (value / searchVo.getLimit()) + 1));//总页数
        pageList.setCurrentPage(searchVo.getPage());//当前页
        pageList.setPageSize(searchVo.getLimit());//每页显示条数


        /**
         * 中文 pubmed
         */
        if ("QF_Study_Cn_Pubmed".equalsIgnoreCase(searchVo.getType())) {
            pageList.setZkydalei(filter_LongNSFC(query, "zkydalei", 50));
            pageList.setAuthor(filter_StrNSFC(query, "auth.keyword", 50));
            pageList.setKeyWord(filter_StrNSFC(query, "keyword.keyword", 50));
            pageList.setDrug(filter_StrNSFC(query, "drug.keyword", 50));
            pageList.setGene(filter_StrNSFC(query, "gene.keyword", 50));
            pageList.setPathway(filter_StrNSFC(query, "pathway.keyword", 50));
            pageList.setDisease(filter_StrNSFC(query, "disease.keyword", 50));
            pageList.setUnitNew(filter_StrNSFCUnit(query, "unit.keyword", 50, searchVo.getUnitNew()));
            pageList.setDept(filter_StrNSFCDEPT(query, "dept.keyword", 50, searchVo.getDept()));
            pageList.setJournal(filter_StrNSFC(query, "journal.keyword", 50));
            pageList.setYear(filter_LongNSFCYear(query, "year", 50));
            pageList.setZkydalei(filter_LongNSFC(query, "zkydalei", 20));
            pageList.setKeyWord(filter_StrNSFC(query, "keyword.keyword", 20));
            pageList.setDrug(filter_StrNSFC(query, "drug.keyword", 20));
            pageList.setGene(filter_StrNSFC(query, "gene.keyword", 20));
            pageList.setPathway(filter_StrNSFC(query, "pathway.keyword", 20));
            pageList.setDisease(filter_StrNSFC(query, "disease.keyword", 20));
            pageList.setUnitNew(filter_StrNSFCUnit(query, "unit.keyword", 20, searchVo.getUnitNew()));
            pageList.setDept(filter_StrNSFCDEPT(query, "dept.keyword", 20, searchVo.getDept()));
            pageList.setJournal(filter_StrNSFC(query, "journal.keyword", 20));
            pageList.setYear(filter_LongNSFCYear(query, "year", 20));
        }

        String uuid = UUID.randomUUID().toString();
        RedisUtil.saveObject(uuid, searchVo);
        pageList.setUuid(uuid);

        return new ResultBean<>(pageList);
    }

    public static List<AggsKeyValue> filter_Str(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEX)
                        .query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ).size(0),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        for (StringTermsBucket bucket : response.aggregations().get("my_aggre").sterms().buckets().array()) {
            long value = bucket.docCount();
            list.add(new AggsKeyValue(bucket.key().stringValue(), value));
        }

        List<AggsKeyValue> collect = list.stream().filter(i -> StringUtil.isNotNull(i)).sorted(Comparator.comparing(AggsKeyValue::getValue).reversed()).collect(Collectors.toList());
        return collect;
    }

    public List<AggsKeyValue> jcr_if(Query query) throws Exception {
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder.index(IndexConfig.CORE_INDEX).query(query).size(0).aggregations("my_agg", i -> i.range(p -> p.field("jcr_if").ranges(AggregationRange.of(o -> o.key("0-3").from(String.valueOf(0)).to(String.valueOf(3))), AggregationRange.of(o -> o.key("3-5").from(String.valueOf(3)).to(String.valueOf(5))), AggregationRange.of(o -> o.key("5-10").from(String.valueOf(5)).to(String.valueOf(10))), AggregationRange.of(o -> o.key("10-20").from(String.valueOf(10)).to(String.valueOf(20))), AggregationRange.of(o -> o.key("20+").from(String.valueOf(20)))))), ArticleEntity.class);
        Buckets<RangeBucket> array = response.aggregations().get("my_agg").range().buckets();
        List<AggsKeyValue> resultList = new ArrayList<>();
        array.array().forEach(i -> {
            String key = i.key();
            long value = i.docCount();
            resultList.add(new AggsKeyValue("" + key, value));
        });
        return resultList;
    }


    public List<AggsKeyValue> filter_LongNSFC(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEXNFSC).query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ).size(0),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        List<LongTermsBucket> array = response.aggregations().get("my_aggre").lterms().buckets().array();

        List<AggsKeyValue> resultList = new ArrayList<>();
        array.forEach(i -> {
            long key = i.key();
            long value = i.docCount();
            resultList.add(new AggsKeyValue("" + key, value));
        });
        List<AggsKeyValue> collect = resultList.stream().sorted(Comparator.comparing(AggsKeyValue::getValue)).collect(Collectors.toList());


        return collect;
    }

    public List<AggsKeyValue> filter_LongNSFCYear(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEXNFSC).query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ).size(0),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        List<LongTermsBucket> array = response.aggregations().get("my_aggre").lterms().buckets().array();

        List<AggsKeyValueInt> resultList = new ArrayList<>();
        List<AggsKeyValue> res = new ArrayList<>();
        array.forEach(i -> {
            Long key = i.key();
            long value = i.docCount();
//            resultList.add(new AggsKeyValue("" + key, value));
            if (key != null) {
                int year = 0;
                try {
                    year = Integer.parseInt(key.toString());
                } catch (Exception e) {
                }
                if (year > 0) {
                    resultList.add(new AggsKeyValueInt(key, value));
                }
            }
        });
        List<AggsKeyValueInt> collect = resultList.stream().sorted(Comparator.comparing(AggsKeyValueInt::getKey).reversed()).collect(Collectors.toList());
        for (AggsKeyValueInt aggsKeyValueInt : collect) {
            AggsKeyValue s = new AggsKeyValue(aggsKeyValueInt.getKey() + "", aggsKeyValueInt.getValue());
            res.add(s);
        }


        return res;
    }

    public List<AggsKeyValue> filter_Long(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEX).query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ).size(0),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        List<LongTermsBucket> array = response.aggregations().get("my_aggre").lterms().buckets().array();

        List<AggsKeyValue> resultList = new ArrayList<>();
        array.forEach(i -> {
            long key = i.key();
            long value = i.docCount();
            resultList.add(new AggsKeyValue("" + key, value));
        });
        List<AggsKeyValue> collect = resultList.stream().sorted(Comparator.comparing(AggsKeyValue::getKey)).collect(Collectors.toList());
        return collect;
    }

    public List<AggsKeyValue> filter_Long_re(Query query, String filter, Integer count) throws Exception {
        ElasticsearchClient client = EsClient.client();
        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEX).query(query)
                        .aggregations("my_aggre", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field(filter).size(count)
                                )
                        ).size(0),
                ArticleEntity.class);
        List<AggsKeyValue> list = new ArrayList<>();
        List<LongTermsBucket> array = response.aggregations().get("my_aggre").lterms().buckets().array();

        List<AggsKeyValue> resultList = new ArrayList<>();
        array.forEach(i -> {
            long key = i.key();
            long value = i.docCount();
            resultList.add(new AggsKeyValue("" + key, value));
        });
        List<AggsKeyValue> collect = resultList.stream().sorted(Comparator.comparing(AggsKeyValue::getKey).reversed()).collect(Collectors.toList());
        return collect;
    }

    /**
     * 柱状图
     */

    @PostMapping("chart_bar")
    public ResultBean<Object> chart_bar(String uuid, String type, Integer count) throws Exception {
        System.out.println("uuid:" + uuid);
        System.out.println("type:" + type);
        ArticleSearchVo searchVo = (ArticleSearchVo) RedisUtil.getObject(uuid);
        BoolQuery.Builder boolQuery = filterService.getBoolQuery(searchVo);
        Query query = boolQuery.build()._toQuery();
        List<AggsKeyValue> chartList = null;


        List<String> keys = new ArrayList<>();
        List<Integer> values = new ArrayList<>();
        List<List<Integer>> ids = new ArrayList<>();
        /**
         * 如果是影响因子,得分段
         */
        if ("score".equalsIgnoreCase(type)) {
            SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder.index(IndexConfig.CORE_INDEX)
                    .query(query)
                    .size(0)
                    .aggregations("my_agg",
                            i -> i.range(p -> p.field("jcr_if")
                                    .ranges(AggregationRange.of(o -> o.key("0").from(String.valueOf(0)).to(String.valueOf(0))),
                                            AggregationRange.of(o -> o.key("0-3").from(String.valueOf(0)).to(String.valueOf(3))),
                                            AggregationRange.of(o -> o.key("3-5").from(String.valueOf(3)).to(String.valueOf(5))),
                                            AggregationRange.of(o -> o.key("5-10").from(String.valueOf(5)).to(String.valueOf(10))),
                                            AggregationRange.of(o -> o.key("10-20").from(String.valueOf(10)).to(String.valueOf(20))),
                                            AggregationRange.of(o -> o.key("20+").from(String.valueOf(20)))
                                    ))), ArticleEntity.class);
            Buckets<RangeBucket> myAgg = response.aggregations().get("my_agg").range().buckets();
            for (RangeBucket rangeBucket : myAgg.array()) {
                keys.add(rangeBucket.key());
                values.add(convertToInt(rangeBucket.docCount()));
            }
            return new ResultBean<>(new IndexPicEntity(keys, values, ids));
        }

        //年份
        if ("year".equalsIgnoreCase(type)) {
            chartList = filter_Long(query, "year", count);
        }
        //引文
//        if ("cita".equalsIgnoreCase(type)) {
//            chartList = filter_Long(query, "cita",count);
//        }
        //作者 这个目前不能用因为没有作者字段
        if ("auth".equalsIgnoreCase(type)) {
            chartList = filter_Str(query, "auth.keyword", count);
        }
        //机构 这个目前没数据
        if ("unit".equalsIgnoreCase(type)) {
            chartList = filter_Str(query, "cn_unit.keyword", count);
        }
        //期刊
        if ("jour".equalsIgnoreCase(type)) {
            chartList = filter_Str(query, "journal.keyword", count);
        }


        for (AggsKeyValue nameIdListSum : chartList) {
            keys.add(nameIdListSum.getKey());
            values.add(Math.toIntExact(nameIdListSum.getValue()));
            //ids.add(nameIdListSum.getIdList());
        }
        return new ResultBean<>(new IndexPicEntity(keys, values, ids));
    }


    public static List<String> xData() {
        List<String> yearList = new ArrayList<>();
        int currentYear = Year.now().getValue();
        for (int i = 0; i < 10; i++) {
            yearList.add("" + (currentYear - i));
        }
        return yearList;
    }


    @GetMapping("hot")
    public ResultBean<Object> hot(String uuid, String chart) throws IOException {
        System.out.println(chart + "获取到的");
        ArticleSearchVo searchVo = (ArticleSearchVo) RedisUtil.getObject(uuid);
        BoolQuery.Builder boolQuery = new BoolQuery.Builder();
        try {
            boolQuery = filterService.getBoolQuery(searchVo);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        boolQuery.mustNot(TermQuery.of(i -> i.field("year").value(2026))._toQuery());

        Query toQuery = boolQuery.build()._toQuery();
        //打印DSL

        SearchResponse<ArticleEntity> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(IndexConfig.CORE_INDEX) // 指定索引名称
                        .query(toQuery)
                        .aggregations("year", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field("year") // 确保使用的是关键字类型字段，如果year是数字则直接使用"year"
                                        .size(10).order(NamedValue.of("_key", SortOrder.Desc))
                                )
                                .aggregations("gene", aggregationBuilder2 -> aggregationBuilder2
                                        .terms(termsAggregationBuilder2 -> termsAggregationBuilder2
                                                .field(chart + ".keyword") // 同样确保使用的是关键字类型字段
                                                .size(10)
                                        )
                                )
                        ),
                ArticleEntity.class
        );
        List<LongTermsBucket> buckets = response.aggregations().get("year").lterms().buckets().array();
        Set<String> xDataSet = new HashSet<>();
        Map<String, Long> geneCountMap = new HashMap<>();
        //第一遍 获取所有的年份和基因
        for (LongTermsBucket bucket : buckets) {
            String doc_count = "" + bucket.docCount();
            String key = "" + bucket.key();
            xDataSet.add(key);
            Aggregate aggregate = bucket.aggregations().get("gene");
            List<StringTermsBucket> bucketsArray = aggregate.sterms().buckets().array();
            for (StringTermsBucket buck : bucketsArray) {
                Long geneCount = buck.docCount();
                String geneKey = buck.key().stringValue();
                geneCountMap.merge(geneKey, geneCount, Long::sum);
            }
        }
        List<String> xData = xDataSet.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        List<String> yData = geneCountMap.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).map(Map.Entry::getKey).collect(Collectors.toList());
        //第二遍,封装数据
        List<Object[]> dataList = new ArrayList<>();
        for (LongTermsBucket bucket : buckets) {
            String doc_count = "" + bucket.docCount();
            String year1 = "" + bucket.key();
            Aggregate aggregate = bucket.aggregations().get("gene");
            List<StringTermsBucket> bucketsArray = aggregate.sterms().buckets().array();
            for (StringTermsBucket buck : bucketsArray) {
                Long geneCount = buck.docCount();
                String geneKey = buck.key().stringValue();
                int x = xData.indexOf(year1);
                if (x != -1) {
                    int y = yData.indexOf(geneKey);
                    if (y != -1) {
                        Object[] dataPoint = {x, y, geneCount};
                        dataList.add(dataPoint);
                    }
                }
            }
        }
        TopicIdeaEntity topicIdeaEntity = new TopicIdeaEntity();
        topicIdeaEntity.setXData(xData);
        topicIdeaEntity.setYData(yData);
        topicIdeaEntity.setData(dataList);
        return new ResultBean<>(topicIdeaEntity);
    }

    public static String encryptionAid(String articleId) {
        byte[] key = "qf3.1415@123@456".getBytes(StandardCharsets.UTF_8);
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
        String encryptHex = aes.encryptHex("" + articleId);
        return encryptHex;
    }

    public static String toTitleCase(String input) {
        StringBuilder titleCase = new StringBuilder();
        boolean nextTitleCase = true;
        for (char c : input.toCharArray()) {
            if (Character.isSpaceChar(c)) {
                nextTitleCase = true;
            } else if (nextTitleCase) {
                c = Character.toTitleCase(c);
                nextTitleCase = false;
            } else {
                c = Character.toLowerCase(c);
            }
            titleCase.append(c);
        }
        return titleCase.toString();
    }

}
